﻿using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Globalization;
using Needle.Utils;

namespace Needle.Framework
{
    /// <summary>
    /// 
    /// </summary>
    public class NestedContainer : Container, INestedContainer, IServiceProvider
    {
        private readonly bool useOwnerSiteAsParentProvider;

        #region constructors...
        /// <summary>
        /// 
        /// </summary>
        public NestedContainer(IComponent owner, bool useOwnerSiteAsParentProvider)
        {
            Ensure.NotNull(owner, "owner");
            this.useOwnerSiteAsParentProvider = useOwnerSiteAsParentProvider;
            this._Owner = owner;
            this._Owner.Disposed += this.OnOwnerDisposed;
        }

        #endregion

        #region overrides
        /// <summary>
        /// Creates a site for a component
        /// </summary>
        protected override ISite CreateSite(IComponent component, string name)
        {
            if (component == null)
                throw new ArgumentNullException("component");
            return new Site(component, this, name);
        }
        /// <summary>
        ///  Disposes the container.
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                this._Owner.Disposed -= this.OnOwnerDisposed;
            }
            base.Dispose(disposing);
        }

        /// <summary>
        /// returns the requested service or null
        /// </summary>
        protected override object GetService(Type serviceType)
        {
            if (serviceType == typeof(INestedContainer))
                return this;
            return base.GetService(serviceType);
        }

        #endregion

        #region event handlers...

        private void OnOwnerDisposed(object sender, EventArgs e)
        {
            base.Dispose();
        }

        #endregion

        #region Properties

        private IComponent _Owner;
        /// <summary>
        /// Owner
        /// </summary>
        public IComponent Owner
        {
            get { return this._Owner; }
        }
        /// <summary>
        /// OwnerName
        /// </summary>
        protected virtual string OwnerName
        {
            get
            {
                if (this._Owner == null)
                    return null;
                ISite site = this._Owner.Site;
                if (site == null)
                    return null;
                INestedSite nestedSite = site as INestedSite;
                if (nestedSite == null)
                    return site.Name;
                return nestedSite.FullName;
            }
        }

        #endregion

        #region IServiceProvider Members

        object IServiceProvider.GetService(Type serviceType)
        {
            return GetService(serviceType);
        }

        #endregion

        #region Site

        private class Site : INestedSite, ISite
        {

            public Site(IComponent component, NestedContainer container, string name)
            {
                this._Component = component;
                this._Container = container;
                this._Name = name;
            }

            private string _Name;
            public string Name
            {
                get { return this._Name; }
                set
                {
                    if (value == null || this._Name == null || !value.Equals(this._Name))
                    {
                        this._Container.ValidateName(this._Component, value);
                        this._Name = value;
                    }
                }
            }

            private IComponent _Component;
            public IComponent Component
            {
                get
                {
                    return this._Component;
                }
            }

            private NestedContainer _Container;
            public IContainer Container
            {
                get
                {
                    return this._Container;
                }
            }

            public bool DesignMode
            {
                get
                {
                    IComponent owner = this._Container.Owner;
                    if (owner == null)
                        return false;
                    ISite site = owner.Site;
                    return site != null && site.DesignMode;
                }
            }

            public string FullName
            {
                get
                {
                    if (this._Name == null)
                        return null;
                    string ownerName = this._Container.OwnerName;
                    string name = this._Name;
                    if (ownerName != null)
                        name = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", ownerName, name);
                    return name;
                }
            }

            #region IServiceProvider Members

            public object GetService(Type service)
            {
                if (service == typeof(ISite))
                    return this;
                return this._Container.GetService(service);
            }

            #endregion

        }

        #endregion
 
        /// <summary></summary>
        protected override IServiceContainer CreateServiceContainer()
        {
            if (useOwnerSiteAsParentProvider)
                return new ServiceContainer(new OwnerSiteAsParentProviderAdapter(this));
            else
                return new ServiceContainer();
        }

        #region "OwnerSiteAsParentProviderAdapter"

        private class OwnerSiteAsParentProviderAdapter : IServiceProvider
        {

            private IComponent owner;
            public OwnerSiteAsParentProviderAdapter(INestedContainer container)
            {
                Ensure.NotNull(container, "container");
                Ensure.NotNull(container.Owner, "container.Owner");
                this.owner  = container.Owner;
            }

            object IServiceProvider.GetService(Type serviceType)
            {
                ISite site = owner.Site;
                if (site != null)
                    return site.GetService(serviceType);
                return null;
            }

        }

        #endregion
    }

}
